﻿using System.Text;
using SdmxMl.Structure;

namespace SasPlugIn.Table
{
    /// <summary>
    /// Class generating SAS syntax for table column definition and value insertion
    /// </summary>
    public class ColumnGenerator
    {
        #region Internal members

        public const int DEFAULT_STRING_LEN = 30;

        #endregion

        #region generation for Table column type

        /// <summary>Generate column of type CHAR </summary>
        /// <param name="sb">Container for string generation</param>
        /// <param name="colName">Name of column</param>
        /// <param name="maxLen">Nb of char</param>
        public void Generate(StringBuilder sb, string colName, int maxLen)
        {
            if (maxLen <= 0)
                maxLen = DEFAULT_STRING_LEN;
            TextFormatType tft = new TextFormatType();
            tft.maxLength = maxLen.ToString();
            tft.TextType = TextTypeType.String;
            Generate(sb, colName, tft);
        }

        /// <summary>
        /// Constructs SAS instruction string for table column creation
        ///
        /// SAS Data Types
        /// Internally, SAS supports two data types for storing data: 
        /// CHAR - fixed-length character data, 32,767-character maximum 
        /// NUM -  double-precision floating-point number 
        /// </summary>
        /// <param name="sb">container for string insertion</param>
        /// <param name="colName">Name for SAS table column</param>
        /// <param name="tft">SDMX Text format type </param>
        public void Generate(StringBuilder sb, string colName, TextFormatType tft)
        {
            switch (tft.TextType)
            {
                case TextTypeType.Integer:
                case TextTypeType.Long:
                    sb.Append(colName).Append(' ').Append("NUM FORMAT=").Append(GetNumericColumnLen(tft, 11)).Append(".0");
                    break;

                case TextTypeType.Short:
                    sb.Append(colName).Append(' ').Append("NUM FORMAT=").Append(GetNumericColumnLen(tft, 6)).Append(".0");
                    break;

                case TextTypeType.BigInteger:
                    sb.Append(colName).Append(' ').Append("NUM FORMAT=").Append(GetNumericColumnLen(tft, 20)).Append(".0");
                    break;

                // num format=DATE9. ,DATE9X,
                case TextTypeType.Date:
                    sb.Append(colName).Append(' ').Append("DATE9X");
                    break;

                // Decimal
                case TextTypeType.Decimal:
                case TextTypeType.Float:
                case TextTypeType.Double:
                    if (string.IsNullOrEmpty(tft.decimals.Trim()) == false && string.IsNullOrEmpty(tft.maxLength.Trim()) == false)
                    {
                        sb.Append(colName).Append(' ').Append("NUM FORMAT=").Append(tft.maxLength.Trim()).Append('.').Append(tft.decimals.Trim());
                    }
                    else
                    {
                        sb.Append(colName).Append(' ').Append("NUM");
                    }
                    break;

                case TextTypeType.Boolean:
                    sb.Append(colName).Append(' ').Append("CHAR(1)");
                    break;

                case TextTypeType.Year:
                    sb.Append(colName).Append(' ').Append("CHAR(4)");
                    break;

                case TextTypeType.String:
                default:
                    int len = GetCharColumnLen(tft);
                    sb.Append(colName).Append(' ').Append("CHAR(").Append(len).Append(")");
                    break;
            }
        }
        #endregion

        #region Generation syntax for value to insert

        /// <summary>
        /// Generate string value for column according to its type information
        /// </summary>
        /// <param name="sb">container for string insertion</param>
        /// <param name="val">field value</param>
        /// <param name="tft">column associated format </param>
        public void InsertValue(StringBuilder sb, string val, TextFormatType tft)
        {
            if (val == null) 
                val = string.Empty;

            switch (tft.TextType)
            {
                case TextTypeType.Integer:
                case TextTypeType.Long:
                case TextTypeType.Short:
                case TextTypeType.BigInteger:
                case TextTypeType.Decimal:
                case TextTypeType.Float:
                case TextTypeType.Double:
                    sb.Append(val);
                    break;

                case TextTypeType.Boolean:
                    if (val.ToLower() == "true" || val == "1")
                        sb.Append("1");
                    else
                        sb.Append("0");
                    break;

                case TextTypeType.Year:
                    if (val.Length > 4)
                        val = val.Substring(0, 4);
                    sb.Append(ValChar(val));
                    break;

                case TextTypeType.Date:
                    //TODO: See with Cedric fore more information
                    sb.Append(ValChar(val));
                    break;

                case TextTypeType.String:
                default:
                    // Trim val to maxlength
                    int len = GetCharColumnLen(tft);
                    if (val.Length > len)
                        val = val.Substring(0, len);
                    sb.Append(ValChar(val));
                    break;
            }
        }
        #endregion

        #region Helpers

        /// <summary>Determines number of char for column</summary>
        /// <param name="tft">text format associated to the column</param>
        /// <returns>number of char to use</returns>
        private int GetCharColumnLen(TextFormatType tft)
        {
            int len = DEFAULT_STRING_LEN;

            // fetch number of char or set to default if nothing specified
            if (string.IsNullOrEmpty(tft.maxLength) == false)
            {
                if (int.TryParse(tft.maxLength.Trim(), out len) == false)
                    len = DEFAULT_STRING_LEN;
            }
            return len;
        }
        /// <summary>Determines number of char for column</summary>
        /// <param name="tft">text format associated to the column</param>
        /// <returns>number of char to use</returns>
        private int GetNumericColumnLen(TextFormatType tft, int defaultLen)
        {
            int len = defaultLen;

            // fetch number of char or set to default if nothing specified
            if (string.IsNullOrEmpty(tft.maxLength) == false)
            {
                if (int.TryParse(tft.maxLength.Trim(), out len) == false)
                    len = defaultLen;
            }

            // Limitation to default
            if (len > defaultLen)
                len = defaultLen;

            return len;
        }


        /// <summary>
        /// Embedd value string between single quote.
        /// Duplicates apostrophe inside text.
        /// </summary>
        /// <param name="val">string value for column row</param>
        /// <returns>Embedded content of string</returns>
        private string ValChar(string val)
        {
            if (val == null)
                return "''";

            val = val.Replace("'", "''");
            return "'" + val + "'";
        }

        #endregion

    }
}
